#include "BuiltInCommand.h"
#include "Command.h"

//Command::Command( string command, char *const argv[] ) { old one
Command::Command( string command) {
	priority = 0;
	command_str = command;
	path_str = findPath(command);
	arguments = new vector<string>();
	arguments->push_back(command);
	append = false;
	isOutputFlag=false;
	isInputFlag=false;
	writeToFile = false;
	buildInCommand = 0;

	//commands
	expressions[C_CD] = "cd";
	expressions[C_JOBS] = "jobs";
	expressions[C_NICE] = "nice";
	expressions[C_PRINTENV] = "printenv";
	expressions[C_WAIT] = "wait";
	expressions[C_EXIT] = "exit";

	// get name of command example: cd, nice
	if( isBuildInCommand( command.c_str() ) ) {
		buildInCommand = new BuiltInCommand( command, arguments );
		buildIn = true;
	} else {
		buildIn = false;
		buildInCommand = 0;
	}
}


Command::~Command() {
	//TODO: implement this
}

string Command::getText(){
	return path_str;
}

void Command::execute(){
	if(!buildIn && !writeToFile){
		executeCommand();
	} else if (writeToFile) {
		int fd, old_stdout;
		old_stdout = dup(STDOUT_FILENO); /* make a backup of the terminal stdout */
		if(append){
			fd = open(outputFile.c_str(), O_APPEND | O_WRONLY | O_CREAT, 0666);
			if ( fd < 0 ){
				cout << "cannot open file: " << outputFile.c_str() << endl;
			} else {

				dup2(fd,STDOUT_FILENO); /*copy the file descriptor fd into standard output*/
				dup2(fd,STDERR_FILENO); /* same, for the standard error */
				close(fd); /* close the file descriptor as we don't need it more  */
				executeCommand();
				dup2(old_stdout, STDOUT_FILENO ); /* put the terminal stdout back */
			}
		} else {
			fd = open(outputFile.c_str(),  O_WRONLY | O_CREAT, 0666);
			if ( fd < 0 ){
				cout << "cannot open file: " << outputFile.c_str() << endl;
			} else {
				dup2(fd,STDOUT_FILENO); /*copy the file descriptor fd into standard output*/
				dup2(fd,STDERR_FILENO); /* same, for the standard error */
				close(fd); /* close the file descriptor as we don't need it more  */
				executeCommand();
				dup2(old_stdout, STDOUT_FILENO ); /* put the terminal stdout back */
			}
		}
	} else if (buildIn){
		executeBuildInCommand();
	}
}


void Command::executeCommand(){
	char * argv[arguments->size()];
	for(unsigned int j=0; j<arguments->size();j++){
		argv[j] = const_cast<char*>(arguments->at(j).c_str()); // o_0 completely fubar
	}
	argv[arguments->size()] = NULL; // null

	pid_t pid = fork();
	if(pid == 0 ){
		int returncode = execv(path_str.c_str(), argv  ); // this boggles the mind, need to check if this actually works
		if(returncode == -1){
			perror("execv error");
		}
	} else if (pid < 0) {
		perror("fork");
	}
}

void Command::addArgument(string newArgument){
	arguments->push_back(newArgument);
}

void Command::setOutputFile(string filename){
	outputFile = filename;
	writeToFile = true;
}

void Command::setInputFile(string filename){
	arguments->push_back(filename);
}

void Command::setPriority( int priority ) {
	this->priority = priority;
}

bool Command::isAppend(){
	return append;
}

void Command::setIsAppend(){
	append = true;
	writeToFile = true;
}

bool Command::isBuildInCommand( const char* command ) {
	for( int i = 0; i < COMMANDS_SIZE; i++ ) {
		if( strcmp(expressions[i], command ) == 0 ) {
			buildIn = true;
			return true;
		}
	}
	return false;
}


void Command::executeBuildInCommand(){
	if( strcmp( command_str.c_str(), "exit" ) == 0 ) {
		exit( 0 );
	} else {
		buildInCommand->executeBuildInCommand();
	}
}

string Command::findPath(string command){
	string env_str = getenv("PATH");
	vector<string> * env = new vector<string>();
	split(env, env_str);
	FILE* tempFile = 0;
	stringstream ss;
	string foundPath;
	for(vector<string>::iterator i = env->begin(); i != env->end(); ++i){
			ss << (*i) << "/" << command;
			string tempStr = ss.str();
			tempFile = fopen(tempStr.c_str(), "rb"); // open file as read only binary
			if(tempFile != 0){
				fclose(tempFile); //
				foundPath = tempStr;
				tempFile = 0;
				ss.str( std::string() );
				ss.clear();
				break;
			}
			ss.str( std::string() );
			ss.clear();
	}
	delete env;

	return foundPath;
}

void Command::split(vector<string> * theVector, string command){
	string single_path;
	stringstream stream(command);
	while( getline(stream, single_path, ':') )
		theVector->push_back(single_path);
}

